package com.superzlc.vjoy.socket;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MessageOp {

	//
	public static DataPackage read(InputStream dis, byte[] buffer) throws IOException, InterruptedException {
		for (int i = 0; i < 6; i++) {
			int k = dis.read();
			if (k == -1)
				throw new EOFException();
			buffer[i] = (byte)k;
		}
		short type = byte2short(buffer, 0);
		int pkgSize = byte2int(buffer, 2);
		int i = 0;
		for (; i < pkgSize; i++) {
			int k = dis.read();
			if (k == -1)
				throw new EOFException();
			buffer[i] = (byte)k;
		}
		DataPackage p = parse(type, pkgSize, buffer);
		return p;
	}

	//
	public static void sendData(OutputStream dos, DataPackage pkg) throws IOException {
		byte[] bytes = pkg.getBytes();
		byte[] bb = new byte[6];
		short2byte(pkg.getType(), bb, 0);
		int2byte(bytes.length, bb, 2);
		dos.write(bb);
		dos.write(bytes);
		dos.flush();
	}

	private static byte[] HeardbeatData = new HeartbeatPackage().getBytes();
	public static void sendHeardbeat(OutputStream dos) throws IOException {
		byte[] bb = new byte[6];
		short2byte(HeartbeatPackage.type, bb, 0);
		int2byte(HeardbeatData.length, bb, 2);
		dos.write(bb);
		dos.write(HeardbeatData);
		dos.flush();
	}


	//
	public static DataPackage parse(short type, int size, byte[] data) {
		DataPackage ret = null;
		switch(type) {
		case SimplePackage.type:
			ret = new SimplePackage();
			break;
		case NormalAckpackage.type:
			ret = new NormalAckpackage();
			break;
		case HeartbeatPackage.type:
			ret = new HeartbeatPackage();
			break;
		case VJoyPackage.type:
			ret = new VJoyPackage();
			break;
		}
		if (ret != null)
			ret.parse(data, size);
		return ret;
	}

	public static abstract class DataPackage {
		public abstract short getType();
		public abstract byte[] getBytes();
		public abstract void parse(byte[] data, int size);
	}

	public static class SimplePackage extends DataPackage {
		public static final short type = 1;
		public String str = "SPKG";
		@Override
		public byte[] getBytes() {
			if (str == null)
				return new byte[0];
			return str.getBytes();
		}
		@Override
		public void parse(byte[] data, int size) {
			str = new String(data, 0, size);
		}
		public String toString() {
			return String.format("[%d] %s", getType(), str);
		}
		@Override
		public short getType() {
			return type;
		}
	}

	public static class NormalAckpackage extends SimplePackage {
		public static final short type = 2;
		{str = "NACKPKG";}
		@Override
		public short getType() {
			return type;
		}
	}

	public static class HeartbeatPackage extends SimplePackage {
		public static final short type = 3;
		{str = "HBPKG";}
		@Override
		public short getType() {
			return type;
		}
	}


	public static class VJoyPackage extends DataPackage {
		public static final short type = 4;
		private int[] asixArr = new int[8];
		// private byte btns1, btns2, btns3, btns4;
		private byte[] bytes = new byte[36];

		public void setAsix(int idx, int v) {
			asixArr[idx] = v;
		}
		public int getAsix(int idx) {
			return asixArr[idx];
		}

		public void setBtn(int idx, boolean pressed) {
			if (idx < 0) {
			} else if (idx < 8) {
				if (pressed) {
					bytes[32] |= 1 << idx;
				} else {
					bytes[32] &= ~(1 << idx);
				}
			} else if (idx < 16) {
				if (pressed) {
					bytes[33] |= 1 << (idx - 8);
				} else {
					bytes[33] &= ~(1 << (idx - 8));
				}
			} else if (idx < 24) {
				if (pressed) {
					bytes[34] |= 1 << (idx - 16);
				} else {
					bytes[34] &= ~(1 << (idx - 16));
				}
			} else if (idx < 32) {
				if (pressed) {
					bytes[35] |= 1 << (idx - 24);
				} else {
					bytes[35] &= ~(1 << (idx - 24));
				}
			} else {
			}
		}
		public boolean getBtnPressed(int idx) {
			if (idx < 0) {
				return false;
			} else if (idx < 8) {
				int m = 1 << idx;
				return (bytes[32] & (m)) == m;
			} else if (idx < 16) {
				int m = 1 << (idx - 8);
				return (bytes[33] & (m)) == m;
			} else if (idx < 24) {
				int m = 1 << (idx - 16);
				return (bytes[34] & (m)) == m;
			} else if (idx < 32) {
				int m = 1 << (idx - 24);
				return (bytes[35] & (m)) == m;
			} else {
				return false;
			}
		}

		public String toString() {
			return String.format("VJoyPackage: %d %d %d %d %d %d %d %d | %s %s %s %s",
					asixArr[0], asixArr[1], asixArr[2], asixArr[3],
					asixArr[4], asixArr[5], asixArr[6], asixArr[7],
					Integer.toBinaryString(bytes[32]),
					Integer.toBinaryString(bytes[33]),
					Integer.toBinaryString(bytes[34]),
					Integer.toBinaryString(bytes[35]));
		}

		@Override
		public byte[] getBytes() {
			for (int i = 0; i < asixArr.length; i++) {
				int2byte(asixArr[i], bytes, i * 4);
			}
			return bytes;
		}

		@Override
		public void parse(byte[] data, int size) {
			for (int i = 0; i < asixArr.length; i++) {
				asixArr[i] = byte2int(data, i * 4);
			}
			int idx = asixArr.length * 4;
			this.bytes[32] = data[idx++];
			this.bytes[33] = data[idx++];
			this.bytes[34] = data[idx++];
			this.bytes[35] = data[idx++];
		}

		private void int2byte(int v, byte[] bytes, int start) {
			bytes[start++] = (byte)((v >>> 24) & 0xFF);
			bytes[start++] = (byte)((v >>> 16) & 0xFF);
			bytes[start++] = (byte)((v >>> 8) & 0xFF);
			bytes[start++] = (byte)((v >>> 0) & 0xFF);
		}

		private int byte2int(byte[] bytes, int start) {
			return (int) ( ((bytes[start] & 0xFF)<<24)
		            |((bytes[start+1] & 0xFF)<<16)
		            |((bytes[start+2] & 0xFF)<<8)
		            |(bytes[start+3] & 0xFF));
		}

		@Override
		public short getType() {
			return type;
		}
	}

	public static class VJoyPackageOld1 extends DataPackage {
		public static final short type = 4;
		private int[] asixArr = new int[8];
		private byte btns1, btns2, btns3, btns4;

		public void setAsix(int idx, int v) {
			asixArr[idx] = v;
		}
		public int getAsix(int idx) {
			return asixArr[idx];
		}

		public void setBtn(int idx, boolean pressed) {
			if (idx < 0) {
			} else if (idx < 8) {
				if (pressed) {
					btns1 |= 1 << idx;
				} else {
					btns1 &= ~(1 << idx);
				}
			} else if (idx < 16) {
				if (pressed) {
					btns2 |= 1 << (idx - 8);
				} else {
					btns2 &= ~(1 << (idx - 8));
				}
			} else if (idx < 24) {
				if (pressed) {
					btns3 |= 1 << (idx - 16);
				} else {
					btns3 &= ~(1 << (idx - 16));
				}
			} else if (idx < 32) {
				if (pressed) {
					btns4 |= 1 << (idx - 24);
				} else {
					btns4 &= ~(1 << (idx - 24));
				}
			} else {
			}
		}
		public boolean getBtnPressed(int idx) {
			if (idx < 0) {
				return false;
			} else if (idx < 8) {
				int m = 1 << idx;
				return (btns1 & (m)) == m;
			} else if (idx < 16) {
				int m = 1 << (idx - 8);
				return (btns2 & (m)) == m;
			} else if (idx < 24) {
				int m = 1 << (idx - 16);
				return (btns3 & (m)) == m;
			} else if (idx < 32) {
				int m = 1 << (idx - 24);
				return (btns4 & (m)) == m;
			} else {
				return false;
			}
		}

		public String toString() {
			return String.format("VJoyPackage: %d %d %d %d %d %d %d %d | %s %s %s %s",
					asixArr[0], asixArr[1], asixArr[2], asixArr[3],
					asixArr[4], asixArr[5], asixArr[6], asixArr[7],
					Integer.toBinaryString(btns1),
					Integer.toBinaryString(btns2),
					Integer.toBinaryString(btns3),
					Integer.toBinaryString(btns4));
		}

		@Override
		public byte[] getBytes() {
			byte[] bytes = new byte[36];

			for (int i = 0; i < asixArr.length; i++) {
				int2byte(asixArr[i], bytes, i * 4);
			}
			int idx = asixArr.length * 4;
			bytes[idx++] = this.btns1;
			bytes[idx++] = this.btns2;
			bytes[idx++] = this.btns3;
			bytes[idx++] = this.btns4;
			return bytes;
		}

		@Override
		public void parse(byte[] data, int size) {
			for (int i = 0; i < asixArr.length; i++) {
				asixArr[i] = byte2int(data, i * 4);
			}
			int idx = asixArr.length * 4;
			this.btns1 = data[idx++];
			this.btns2 = data[idx++];
			this.btns3 = data[idx++];
			this.btns4 = data[idx++];
		}

		@Override
		public short getType() {
			return type;
		}
	}

	private static void int2byte(int v, byte[] bytes, int start) {
		bytes[start++] = (byte)((v >>> 24) & 0xFF);
		bytes[start++] = (byte)((v >>> 16) & 0xFF);
		bytes[start++] = (byte)((v >>> 8) & 0xFF);
		bytes[start++] = (byte)((v >>> 0) & 0xFF);
	}

	private static int byte2int(byte[] bytes, int start) {
		return (int) ( ((bytes[start] & 0xFF)<<24)
	            |((bytes[start+1] & 0xFF)<<16)
	            |((bytes[start+2] & 0xFF)<<8)
	            |(bytes[start+3] & 0xFF));
	}

	private static void short2byte(short v, byte[] bytes, int start) {
		bytes[start++] = (byte)((v >>> 8) & 0xFF);
		bytes[start++] = (byte)((v >>> 0) & 0xFF);
	}

	private static short byte2short(byte[] bytes, int start) {
		return (short) (((bytes[start+0] & 0xFF)<<8)
	            |(bytes[start+1] & 0xFF));
	}

	public static void main(String[] args) {
		VJoyPackage t = new VJoyPackage();
		t.setBtn(1, true);
		t.setBtn(0, true);
		System.out.println(t.toString());
		System.out.println(t.getBtnPressed(1));
		System.out.println(t.getBtnPressed(2));

	}
}
